package com.bjsxt.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.alicloud.sms.ISmsService;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bjsxt.domain.Config;
import com.bjsxt.domain.Sms;
import com.bjsxt.mapper.SmsMapper;
import com.bjsxt.service.ConfigService;
import com.bjsxt.service.SmsService;
import com.bjsxt.util.SmsStringFormatUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SmsServiceImpl extends ServiceImpl<SmsMapper, Sms> implements SmsService {

    /**
     * 阿里云短信发送服务的业务层对象
     */
    @Autowired
    private ISmsService smsService;

    /**
     * 平台参数配置业务层对象
     */
    @Autowired
    private ConfigService configService;

    /**
     * String 类型的 redis 客户端操作对象
     */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 发送一个短信
     *
     * @param sms 短信信息对象
     * @return true: 发送成功 false: 发送失败
     */
    @Override
    public boolean sendSms(Sms sms) {
        // 日志打印发送短信的对象
//        log.info("发送短信：{}", JSON.toJSONString(sms, true));
        // 利用 Sms 短信信息对象构建发送请求
        SendSmsRequest sendSmsRequest = buildSmsRequest(sms);
        // 调用阿里云短信业务进行短信的发送
        try {
            // 调用方法对短信进行发送
            SendSmsResponse sendSmsResponse = smsService.sendSmsRequest(sendSmsRequest);
            // 日志打印短信发送的结果
//            log.info("发送短信的结果：{}", JSON.toJSONString(sendSmsResponse, true));
            // 获取短信发送的结果
            String code = sendSmsResponse.getCode();
            // 如果返回的 code 是 OK 那么发送成功
            if ("OK".equals(code)) {
                // 对当前的 sms 对象的发送状态改为发送成功
                sms.setStatus((byte) 1);
                // 调用方法进行保存，并返回报错成功或者失败的值
                return save(sms);
            }
        } catch (ClientException e) {
            throw new RuntimeException(e);
        }
        // 返回发送失败
        return false;
    }

    /**
     * 构建发送短信的请求对象
     * 这里在构建验证码数据的时候会向 Redis 中增加一条验证码数据规则为：key:SMS:VERIFY_OLD_PHONE（短信模板 ID）:15827293117（手机号） value: 123456（验证码值）
     *
     * @param sms 短信信息对象
     * @return Alibaba SMS 短信服务请求发送对象
     */
    private SendSmsRequest buildSmsRequest(Sms sms) {
        // 创建发送短信的请求对象
        SendSmsRequest sendSmsRequest = new SendSmsRequest();
        // 设置发送的短信发送给那个手机号
        sendSmsRequest.setPhoneNumbers(sms.getMobile());
        // 根据规则代码查询配置数据,这里这个值在平台配置类中配置的签名
        Config signConfig = configService.getByCode("SIGN");
        // 设置发送短信的签名
        sendSmsRequest.setSignName(signConfig.getValue());
        // 根据规则代码查询配置数据,这里这个值在平台配置类中配置的模板 CODE
        Config codeConfig = configService.getByCode(sms.getTemplateCode());
        // 如果根据参数进行查询为空进行报错
        Assert.notNull(codeConfig, "模板 CODE 不存在");
        // 设置模板 CODE
        sendSmsRequest.setTemplateCode(codeConfig.getValue());
        // 创建发送的验证码值，使用工具类生成 6 位验证码
        String code = RandomUtil.randomNumbers(6);
        // 将生成的验证码放入到 redis 中，存储的规则：key:SMS:VERIFY_OLD_PHONE:15827293117 value: 123456
        stringRedisTemplate.opsForValue().set("SMS:" + sms.getTemplateCode() + ':' + sms.getMobile(), code, 5, TimeUnit.MINUTES);
        // 设置验证码的 json 对象，这里是发送给 阿里云的
        sendSmsRequest.setTemplateParam("{\"code\":\"" + code + "\"}");
        // 下面是存入系统的
        // 构建 map 对字符串中的文字进行替换
        Map<String, String> rules = new HashMap<String, String>();
        // 放入签名数据
        rules.put("${sign}", signConfig.getValue());
        // 放入验证码数据
        rules.put("${code}", code);
        // 拼接存储到系统的信息内容，您的验证码为：${code}，请勿泄露于他人！ -> sign:您的验证码为：code，请勿泄露于他人！
        String content = SmsStringFormatUtils.parse(codeConfig.getDesc(), rules);
        // 设置短信的发送内容
        sms.setContent(content);
        // 返回短信请求对象
        return sendSmsRequest;
    }
}
